﻿using System;
using System.Configuration;
using LinqToLfsWorld.Configuration;
using LinqToLfsWorld.Queries;
using LinqToLfsWorld.Linq;

namespace LinqToLfsWorld
{

	/// <summary>
	/// The starting point for your queries to LFSWorld.
	/// </summary>
	public class LfsWorldContext : IDisposable
	{

		#region Initialisation and Constructors

		/// <summary>
		/// Creates a new instance of LfsWorldContext.
		/// </summary>
		public LfsWorldContext()
		{
			AuthenticationMode = AuthMode.Unassigned;
			Initialise();
		}

		/// <summary>
		/// Creates a new instance of LfsWorldContext, using the IdentKey authentication mode.
		/// </summary>
		/// <param name="identKey"></param>
		public LfsWorldContext(string identKey)
		{
			IdentKey = identKey;
			AuthenticationMode = AuthMode.IdentKey;
			Initialise();
		}

		/// <summary>
		/// Creates a new instance of LfsWorldContext, using the username and password authentication mode.
		/// </summary>
		/// <param name="username">Your LFS username</param>
		/// <param name="password">Your LFS online password</param>
		public LfsWorldContext(string username, string password)
		{
			Username = username;
			Password = password;
			AuthenticationMode = AuthMode.UsernamePassword;
			Initialise();
		}

		/// <summary>
		/// Creates a new cache key for pubstat response caching
		/// </summary>
		private void Initialise()
		{
			CacheTime = TimeSpan.FromSeconds(5);
			CachingEnabled = true;
			PremiumPubstatAccess = false;

			// Handle the 'request made' event from the global request handler.
			// Note: Dispose must be called once the context is finished with so that this
			// event can be unregistered.
			LfsWorldRequest.Instance.RequestMade += new LfsRequestHandler(LfsWorldRequest_RequestMade);

			LoadConfiguration();

			ValidateAuthentication();
		}

		#endregion


		#region Configuration

		/// <summary>
		/// Gets the configuration settings from the configuration file.
		/// </summary>
		public LFSWConfigurationSection Configuration
		{
			get
			{
				if (_Configuration == null)
					_Configuration = (LFSWConfigurationSection)ConfigurationManager.GetSection("lfswConfiguration");
				return _Configuration;
			}
		}
		LFSWConfigurationSection _Configuration = null;

		/// <summary>
		/// Loads configuration settings, if any have been provided.
		/// </summary>
		private void LoadConfiguration()
		{
			if (Configuration != null)
			{
				if (!(string.IsNullOrEmpty(Configuration.IdentKey)))
					IdentKey = Configuration.IdentKey;

				if (Configuration.PremiumPubstatAccess != null)
					PremiumPubstatAccess = (bool)Configuration.PremiumPubstatAccess;

				if (!(string.IsNullOrEmpty(Configuration.Username)))
					Username = Configuration.Username;

				if (!(string.IsNullOrEmpty(Configuration.Password)))
					Password = Configuration.Password;

				if (Configuration.CachingEnabled != null)
					CachingEnabled = (bool)Configuration.CachingEnabled;

				if (Configuration.CacheSeconds != null)
					CacheTime = TimeSpan.FromSeconds((int)Configuration.CacheSeconds);

				AuthenticationMode = Configuration.AuthenticationMode;
			}
		}

		#endregion


		#region Events

		/// <summary>
		/// Fires whenever any request is made through this context.
		/// </summary>
		public event LfsRequestHandler RequestMade;

		/// <summary>
		/// Handles the RequestMade event as fired from the internal request handler, and forwards it
		/// to the client.
		/// </summary>
		/// <param name="sender">The instance of the request handler</param>
		/// <param name="response">The response which was made.</param>
		void LfsWorldRequest_RequestMade(object sender, LfsWorldResponse response)
		{
			if (RequestMade != null)
				RequestMade(this, response);
		}

		#endregion


		#region Authentication

		/// <summary>
		/// Gets the authentication mode
		/// </summary>
		public AuthMode AuthenticationMode { get; private set; }

		/// <summary>
		/// Gets the ident key, if AuthenticationMode is set to "IdentKey".
		/// </summary>
		public string IdentKey { get; private set; }

		/// <summary>
		/// Gets the username, if the AuthenticationMode is set to "UsernamePassword".
		/// </summary>
		public string Username {get; private set;}

		/// <summary>
		/// Gets the password, if the AuthenticationMode is set to "UsernamePassword".
		/// </summary>
		public string Password { get; private set; }

		/// <summary>
		/// Validates the authentication settings, and throws an exception if sufficient
		/// settings to make a request have not been supplied.
		/// </summary>
		private void ValidateAuthentication()
		{
			if (AuthenticationMode == AuthMode.Unassigned)
				throw new Exception("Authentication Mode not valid.");

			if (AuthenticationMode == AuthMode.IdentKey)
			{
				if (string.IsNullOrEmpty(IdentKey))
					throw new Exception("Invalid ident key");

				LfsWorldRequest.Instance.RequestCacheKey = this.IdentKey;
			}
			else if (AuthenticationMode == AuthMode.UsernamePassword)
			{
				if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
					throw new Exception("Invalid username and/or password");

				LfsWorldRequest.Instance.RequestCacheKey = this.Username + "+" + this.Password;
			}
		}

		#endregion


		#region Caching Members

		/// <summary>
		/// Gets or sets the ability to use in-memory caching of results.
		/// </summary>
		public bool CachingEnabled
		{
			get { return LfsWorldRequest.Instance.CachingEnabled; }
			set { LfsWorldRequest.Instance.CachingEnabled = value; }
		}

		/// <summary>
		/// Gets whether or not caching is available on the host machine.
		/// </summary>
		public bool CachingAvailable
		{
			get
			{
				return LfsWorldRequest.Instance.CachingAvailable;
			}
		}

		/// <summary>
		/// Gets whether or not the context can make a request to the pubstat server yet.
		/// </summary>
		public bool CanMakeRequest
		{
			get
			{
				return LfsWorldRequest.Instance.CanMakeRequest;
			}
		}

		/// <summary>
		/// If true, premium pubstat access is assumed and global requests will not be kerbed by the request manager.
		/// This is set to false by default.
		/// </summary>
		public bool PremiumPubstatAccess
		{
			get
			{
				return !(LfsWorldRequest.Instance.EnableRequestKerbing);
			}
			set
			{
				LfsWorldRequest.Instance.EnableRequestKerbing = !(value);
			}
		}

		/// <summary>
		/// Gets or sets the time the results should be cached for.
		/// </summary>
		public TimeSpan CacheTime { 
			get 
			{
				return LfsWorldRequest.Instance.CacheTime;
			} 
			
			set 
			{
				LfsWorldRequest.Instance.CacheTime = value;
			} 
		}

		#endregion


		#region Queryable Properties

		/// <summary>
		/// Queries host status information from LfsWorld.
		/// </summary>
		public Query<HostStatus> HostStatus
		{
			get
			{
				_HostStatus = _HostStatus ?? new Query<HostStatus>(new HostStatusQuery(this));
				return _HostStatus;
			}
		}
		Query<HostStatus> _HostStatus = null;

		/// <summary>
		/// Queries the hotlaps from LFSWorld.
		/// </summary>
		public Query<Hotlap> Hotlaps 
		{ 
			get 
			{
				if (_Hotlaps == null)
					_Hotlaps = new Query<Hotlap>(new HotlapsQuery(this));
				return _Hotlaps;				
			} 
		}
		Query<Hotlap> _Hotlaps = null;

		/// <summary>
		/// Queries the personal bests from LFSWorld.
		/// </summary>
		public Query<PersonalBest> PersonalBests
		{
			get
			{
				if (_PersonalBests == null)
					_PersonalBests = new Query<PersonalBest>(new PersonalBestsQuery(this));
				return _PersonalBests;
			}
		}
		Query<PersonalBest> _PersonalBests = null;

		/// <summary>
		/// Queries the world records from LFSWorld.
		/// </summary>
		/// <value>The world records.</value>
		public Query<WorldRecord> WorldRecords
		{
			get
			{
				if (_WorldRecords == null)
					_WorldRecords = new Query<WorldRecord>(new WorldRecordQuery(this));
				return _WorldRecords;
			}
		}
		Query<WorldRecord> _WorldRecords = null;

		/// <summary>
		/// Queries the hotlap chart from LFSWorld.
		/// </summary>
		/// <value>The hotlap chart.</value>
		public Query<HotlapChartEntry> HotlapChart
		{
			get
			{
				if (_HotlapChart == null)
					_HotlapChart = new Query<HotlapChartEntry>(new HotlapChartQuery(this));
				return _HotlapChart;
			}
		}
		Query<HotlapChartEntry> _HotlapChart;



		/// <summary>
		/// Queries LFSWorld for a single racer's statistics.
		/// </summary>
		public Query<RacerStats> RacerStats
		{
			get
			{
				if (_RacerStats == null)
					_RacerStats = new Query<RacerStats>(new RacerStatsQuery(this));
				return _RacerStats;
			}
		}
		Query<RacerStats> _RacerStats = null;
		
		/// <summary>
		/// Queries the hosts from LfsWorld
		/// </summary>
		public Query<Host> Hosts
		{
			get
			{
				if (_Hosts == null)
					_Hosts = new Query<Host>(new HostsQuery(this));
				return _Hosts;
			}
		}
		Query<Host> _Hosts = null;

		/// <summary>
		/// Queries the teams from LfsWorld.
		/// </summary>
		public Query<Team> Teams
		{
			get
			{
				if (_Teams == null)
					_Teams = new Query<Team>(new TeamQuery(this));
				return _Teams;
			}
		}
		Query<Team> _Teams = null;


		/// <summary>
		/// Queries the racer fuel levels from LFSWorld.
		/// </summary>
		public Query<RacerFuel> RacerFuels
		{
			get
			{
				if (_RacerFuels == null)
					_RacerFuels = new Query<RacerFuel>(new RacerFuelQuery(this));
				return _RacerFuels;
			}
		}
		Query<RacerFuel> _RacerFuels = null;


		/// <summary>
		/// Queries the Hotlap Log from LFSWorld.
		/// </summary>
		public Query<HotlapLogEntry> HotlapLog
		{
			get
			{
				if (_HotlapLog == null)
					_HotlapLog = new Query<HotlapLogEntry>(new HotlapLogQuery(this));
				return _HotlapLog;
			}
		}
		Query<HotlapLogEntry> _HotlapLog = null;

		#endregion


		#region IDisposable Members

		/// <summary>
		/// Disposes the LfsWorldContext instance.
		/// </summary>
		public void Dispose()
		{
			// Unregister with the internal request managaer, or else wierd things could happen.
			LfsWorldRequest.Instance.RequestMade -= new LfsRequestHandler(this.LfsWorldRequest_RequestMade);
		}

		#endregion

	}

}
